package licode.unisop.auth.adapter.web;

import cn.hutool.core.util.StrUtil;
import licode.unisop.auth.application.uniclient.SopAuthorExecutor;
import licode.unisop.auth.application.vo.CaptchaIn;
import licode.unisop.auth.client.api.CodeValidateService;
import licode.unisop.auth.client.info.*;
import licode.unisop.auth.client.vo.CaptchaVO;
import licode.unisop.client.info.*;
import licode.unisop.client.utils.PlatformCheck;
import licode.unisop.client.utils.SopAuthConst;
import licode.unisop.client.utils.SopRequestParse;
import licode.unisop.client.vo.SopAuthPlatform;
import licode.unisop.client.vo.SopAuthResult;
import licode.unisop.client.vo.SopAuthUser;
import licode.unisop.platform.client.api.SiPlatformAuthService;
import licode.unisop.platform.client.vo.PlatAuthVO;
import licode.unisop.provider.info.SiPlatIdFind;
import licode.unisop.types.AuthConst;
import licode.unisop.unilite.result.UniResult;
import licode.unisop.unilite.result.UniReturnResult;
import licode.unisop.unilite.utils.TryMethodCall;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 定义A通过用户名和密码直接认证的接口
 *
 * @author WR
 */
@Slf4j
@UniReturnResult
@RestController
@RequestMapping("/uni-auth/v1/oauth")
public class AuthorUniController {
    @Resource
    private HttpServletRequest request;
    
    @Resource
    private PlatformCheck platformCheck;

    @Resource
    private SiPlatformAuthService siPlatformAuthService;

    @Resource
    private SopAuthorExecutor sopAuthorExecutor;

    @Resource
    private CodeValidateService codeValidateService;

    private String getSsoToken() {
        return SopRequestParse.getValueOrDefault(request, AuthConst.UNI_MASTER_SESSION, "");
    }

    private SopTokenInfo getAccessTokenInfo() {
        SopTokenInfo tokenInfo;

        tokenInfo = SopRequestParse.getAccessTokenInfo(request);
        tokenInfo.setSsoToken(getSsoToken());

        return tokenInfo;
    }

    private SopTokenInfo getRefreshTokenInfo() {
        return SopRequestParse.getRefreshTokenInfo(request);
    }

    private SopAccessInfo getTokenRequest() {
        return SopRequestParse.getSsoAuthToken(request);
    }

    private CaptchaIn getCaptchaIn(Integer noCaptcha) {
        return CaptchaIn.builder()
                .noCaptcha(noCaptcha)
                .key(SopRequestParse.getValue(request, SopAuthConst.CAPTCHA_KEY))
                .code(SopRequestParse.getValue(request, SopAuthConst.CAPTCHA_CODE))
                .build();
    }

    /**
     * 获取验证码
     *
     * @return 验证码
     */
    @PostMapping("/captcha/code")
    public UniResult<CaptchaVO> getCaptchaCode() {
        return TryMethodCall.exec(null, () -> codeValidateService.buildCaptcha());
    }

    /**
     * 通过平台ID，获取平台的TOKEN
     *
     * @param platformId 平台ID
     * @return 平台TOKEN
     */
    @PostMapping("/platform-token/{platformId}")
    public UniResult<String> getPlatformToken(@PathVariable("platformId") String platformId) {
        return TryMethodCall.exec("", () -> {
            platformCheck.checkPlatform(request);
            PlatAuthVO plat = siPlatformAuthService.fromId(SiPlatIdFind.builder()
                    .platformId(platformId).build());
            return null == plat ? "" : plat.getToken();
        });
    }

    @PostMapping("/auth-platform")
    public UniResult<SopAuthPlatform> authorize(@RequestBody PlatformAuthIn platformAuthIn) {
        return TryMethodCall.exec(null, () -> {
            platformCheck.checkPlatform(request);
            return sopAuthorExecutor.registerPlatform(platformAuthIn);
        });
    }

    @PostMapping("/user/password/update")
    public UniResult<String> updatePassword(@RequestBody SopUpdatePasswordIn passwordIn) {
        return TryMethodCall.exec(null, () -> {
            platformCheck.checkPlatform(request);
            passwordIn.setAccessToken(SopRequestParse.getAccessToken(request));
            return sopAuthorExecutor.updatePassword(passwordIn);
        });
    }

    @PostMapping("/authorize")
    public UniResult<SopAuthResult> authorize(@RequestBody(required = false) AuthAccount authAccount) {
        return TryMethodCall.exec(null,
                () -> {
                    platformCheck.checkPlatform(request);
                    return sopAuthorExecutor.authorize(getCaptchaIn(authAccount.getNoCaptcha()),
                            buildLoginContext(getTokenRequest(), authAccount));
                }
        );
    }

    @PostMapping("/authorize-direct")
    public UniResult<SopAuthResult> authorizeDirect(@RequestBody SopAuthDirectIn authAccount) {
        return TryMethodCall.exec(null,
                () -> {
                    platformCheck.checkModalToken(request);
                    return sopAuthorExecutor.authorizeDirect(authAccount);
                });
    }

    @PostMapping("/auto")
    public UniResult<SopAuthResult> autoSso() {
        return TryMethodCall.exec(null, () -> {
                    platformCheck.checkPlatform(request);
                    AuthDetailIn auth = buildAutoDetail(getTokenRequest());
                    auth.setSsoToken(getSsoToken());
                    return sopAuthorExecutor.autoSso(auth);
                }
        );
    }

    /**
     * 对指定的用户进行认证，认证成功后返回用户的基本信息（包括令牌和用户信息）
     *
     * @param authAccount 用户验证信息
     * @return 用户凭证和基本信息
     */
    @PostMapping("/login")
    public UniResult<SopAuthUser> login(@RequestBody(required = false) AuthAccount authAccount) {
        return TryMethodCall.exec(null,
                () -> {
                    platformCheck.checkPlatform(request);
                    return sopAuthorExecutor.login(getCaptchaIn(authAccount.getNoCaptcha()),
                            buildLoginContext(getTokenRequest(), authAccount));
                });
    }

    @PostMapping("/login-direct")
    public UniResult<SopAuthUser> login(@RequestBody SopAuthDirectIn authAccount) {
        return TryMethodCall.exec(null,
                () -> {
                    platformCheck.checkModalToken(request);
                    return sopAuthorExecutor.loginDirect(authAccount);
                });
    }

    /**
     * 使用code换取登录令牌
     *
     * @return 用户凭证和基本信息
     */
    @PostMapping("/token")
    public UniResult<SopAuthUser> authUserByCode() {
        return TryMethodCall.exec(null,
                () -> {
                    SopAccessInfo accessInfo = getTokenRequest();
                    platformCheck.checkPlatform(request);
                    return sopAuthorExecutor.token(LoginCodeIn.builder()
                            .code(accessInfo.getCode())
                            .state(accessInfo.getState())
                            .authDetail(AuthDetailIn.builder()
                                    .realmId(accessInfo.getRealmId())
                                    .tenantId(accessInfo.getTenantId())
                                    .terminal(accessInfo.getTerminalId())
                                    .platformId(accessInfo.getPlatformId())
                                    .clientId(accessInfo.getClientId())
                                    .clientSecret(accessInfo.getClientSecret())
                                    .build())
                            .build());
                });
    }

    /**
     * 通过刷新令牌来重新刷新用户登录信息，成功后返回用户信息
     *
     * @return 用户凭证和基本信息
     */
    @PostMapping("/refresh")
    public UniResult<Credential> refreshToken() {
        return TryMethodCall.exec(null, () ->
                sopAuthorExecutor.refresh(getRefreshTokenInfo()));
    }

    /**
     * 通过登录令牌来移除已经登录的用户信息
     *
     * @return 成功返回true；否则返回false
     */
    @PostMapping("/logout")
    public UniResult<Boolean> logout() {
        return TryMethodCall.exec(false, () -> {
            platformCheck.checkPlatform(request);
            sopAuthorExecutor.logout(getAccessTokenInfo());
            return true;
        });
    }

    @PostMapping("/user/logout")
    public UniResult<Boolean> logoutUser(@RequestBody SopUserIdIn req) {
        return TryMethodCall.exec(false, () -> {
            sopAuthorExecutor.logoutUser(req);
            return true;
        });
    }

    @PostMapping("/session/logout")
    public UniResult<Boolean> logoutSession() {
        return TryMethodCall.exec(false, () -> {
            sopAuthorExecutor.logoutSession(getAccessTokenInfo());
            return true;
        });
    }

    private LoginContextIn buildLoginContext(SopAccessInfo accessInfo, AuthAccount authAccount) {
        if (StrUtil.isBlank(authAccount.getState())) {
            authAccount.setState(accessInfo.getState());
        }
        return LoginContextIn.builder()
                .authMode(authAccount.getAuthMode())
                .auth(IdentityIn.builder()
                        .authCode(authAccount.getAuthCode())
                        .authType(authAccount.getAuthType())
                        .authData(authAccount.getAuthData())
                        .build())
                .account(AccountIn.builder()
                        .realmId(accessInfo.getRealmId())
                        .category(authAccount.getCategory())
                        .account(authAccount.getAccount())
                        .data("")
                        .build())
                .authDetail(AuthDetailIn.builder()
                        .realmId(accessInfo.getRealmId())
                        .platformId(accessInfo.getPlatformId())
                        .terminal(accessInfo.getTerminalId())
                        .tenantId(accessInfo.getTenantId())
                        .clientId(accessInfo.getClientId())
                        .clientSecret(accessInfo.getClientSecret())
                        .state(authAccount.getState())
                        .build())
                .build();
    }

    private AuthDetailIn buildAutoDetail(SopAccessInfo accessInfo) {
        return AuthDetailIn.builder()
                .realmId(accessInfo.getRealmId())
                .platformId(accessInfo.getPlatformId())
                .terminal(accessInfo.getTerminalId())
                .tenantId(accessInfo.getTenantId())
                .clientId(accessInfo.getClientId())
                .clientSecret(accessInfo.getClientSecret())
                .build();
    }
}
